/**
 * 
 */
package logic;

import io.IO;
import io.ImageReader;

import java.awt.image.BufferedImage;
import java.io.FileNotFoundException;
import java.io.IOException;

import datamodel.huffman.tree.Tree;
import logic.huffman.HuffmanCode;
import logic.huffman.HuffmanTree;
import logic.trie.TrieCode;


/**
 * Questions (see exercise sheet for English version)
 * 
 * Frage 1: Vergleichen Sie die Dateigröße ihres mit Huffman codierten Bildes und des Orginalbildes.
 * Codieren Sie dazu jeweils die Bilder in einen BinärString (einmal normal RGB, einmal Huffman) und geben Sie diesen mit BinaryIO aus.
 * Die Methoden dazu sind: writeBinaryString, welche ihren HuffmanString bekommt; writeImageBinaryString bekommt das OrginalBild
 * Erklären Sie den Unterschied. Berechnen Sie aus der Dateigröße des Huffman- Bildes die mittlere
 * Anzahl Bits pro Pixel.
 * 
 * TODO: Answer
 * Größe von Datei mit RGB-Binärstring: 256 (64*4) Byte
 * Größe von Datei mit Huffman-Code: 19 Byte
 * Der deutliche Unterschied liegt in der unterschiedlichen Häufigkeit der Farben. Beispielsweise kommt
 * Grün 23-mal vor. Im resultierenden Huffman-Baum wird es folglich auch mit dem kürzesten String
 * (nur 1 Bit) kodiert.
 * Daraus resultiert gegenüber der RGB-Darstellung, in der Grün 32 Bit benötigt, schon ein Unterschied um 31 Bit.
 * Bei 23-maligem Auftreten also 23*31 Bit ~ 90 Byte. Analog lässt sich der weitere Unterschied für die
 * anderen Farben nachvollziehen.
 * 19*8 / 64 b = 2,375b
 * 
 * 
 * 
 * Frage 2: 
 * Was fällt Ihnen an den beiden decodierten Bildern auf, wie unterscheiden sich diese?
 * Erklären Sie die auftretenden Unterschiede in den beiden Bildern.
 * Die Method zum Speichern eines BufferedImage findet sich in der IO Klasse: saveImage erstellt aus ihrem decodierten BufferedImage eine png Datei.
 * 
 * TODO: Answer
 * Die unterschiedliche Reihenfolge der Methodenaufrufe führt zu unterschiedlichen Ergebnissen.
 * Bei der Reihenfolge encryptImage(), compress(1), decryptImage() erhält man ein Bild, das sich an an mehr Stellen
 * vom Originalbild unterscheidet als da, wo im Originalbild die nun "abgeschnittenen" Farben waren. Bei der Kodierung
 * werden noch alle Farben aus dem Originalbild kodiert. Dann wird der Baum verkleinert, so dass decryptImage()
 * auf Bits im String stößt, die Pfade beschreiben, die im abgeschnittenen Baum nicht mehr vorhanden sind.
 * decryptImage() stößt auf die Farbe weiß und beginnt mit den restlichen Bits (die vormals den restlichen Pfad
 * der abgeschnittenen Farbe kodierten) wieder von oben durch den Baum zu suchen. Dabei werden Kodierungs-
 * strings für die einzelnen Farben ineinander verschoben.
 * Die zweite Reihenfolge kodiert bereits mit dem gekürzten Baum (bzw. dem davon aktualisierten Huffman-Code),
 * so dass bei der Dekodierung auch alles wieder ohne Verschiebungen umgekehrt werden kann.
 * Fazit: Beim ersten Mal "passt" also der Code nicht zum Baum, beim zweiten Mal schon. 
 *
 * Frage 3:
 * Vergleichen Sie die Dateigröße des Originalbildes mit dem TrieCode- Bild (verlustfrei).
 * Warum sind beide bei unterschiedlicher Codierung identisch? Geben Sie eine Formel zur Berechnung
 * des Speicherbedarfs eines verlustfrei trie- codierten Bildes an.
 * Ziehen Sie nun auch das zweite Bild in ihren Vergleich mit ein. Aktualisieren Sie ihre Formel, sodass
 * diese auch die Stufen des Trees berücksichtigt.
 * Verwenden Sie wieder die Methode writeBinaryString aus IO um ihre codierten Bilder zu speichern.
 * 
 * Betrachten Sie nun die beiden decodierten Bilder. Erklären Sie Ihre Beobachtungen.
 * Worauf sind diese zurückzuführen?
 * 
 * TODO: Answer
 * Die Dateigrößen sind identisch, da eine Farbe im Trie mit 4*8b=32b kodiert wird, genau wie ein RGB.
 * Größe in Byte = Pixelanzahl * 4*8b / 8
 * Durch Kompression ändert sich nichts an der Dateigröße, da abgeschnittene Kodierungsbits durch "1000" ersetzt werden.
 * zunehmend dunkler: Originalbild, Kompression 3, Kompression 6
 * (Zwei Farben, die sich nur in den lsb unterscheiden, werden nach compress(1) zur gleichen Farbe.)
 * also: Die bei der Kompression abgeschnittenen Bits werden mit "1000" ersetzt, was die Farben dunkler macht.
 * 
 * Frage 4:
 * Wie erklärt sich der Unterschied im Orginalbild und dem wieder decodierten Bild?
 * 
 * TODO: Answer
 * Die Farben im wieder dekodierten Bild sind auch dunkler, da die hellen Farben aus test2.png
 * in test.png nicht vorkommen. Erzeugt man einen Trie aus dem test.png, werden die in test2.png fehlenden Farben
 * auf die gleiche Art und Weise wie bei der Kompression ergänzt ("1000").
 * 
 *
 * Frage 5:
 * Vergleichen Sie die Geschwindigkeit beider Suchmethoden. Geben Sie eine theoretische Erklärung für ihre Beobachtungen an.
 * 
 * TODO: Answer
 * HuffmanCode.containsColor() benutzt DFS. Dies hat nach Skript O(|V| + |E|) V: Knoten, E: Kanten
 * TrieCode.containsColor() ist abhängig von der Trie-Tiefe, die immer 8 ist. Also O(8) = O(1) -> konstante Zeit 
 */

/**
 * Interaction with the application
 * 
 * @author Jakob Karolus, Kevin Munk
 * @version 1.0
 * 
 */
public class Main {

	public static void main(String[] args) {
		//TODO: You may build your images, etc. using this class
		try {
			
			
			// Frage 1 - START, Aufgabe 3 
			Tree ht = HuffmanTree.getHuffmanTree("test2.png");
			HuffmanCode hc = new HuffmanCode(ht);
			
			// with Huffman: code and decode and save as .png
			String test2PNGEncoded = hc.encryptImage(new ImageReader("test2.png"));
			// create file (binary string of RGBs)
			IO.writeBinaryString("test2_HF.bin", test2PNGEncoded);
			IO.writeImageBinaryString("test2_RGB.bin", new ImageReader("test2.png"));
			// Frage 1 - ENDE
			
			
			// Frage 2 - START, Punkt 5. in PDF:
			ht = HuffmanTree.getHuffmanTree("test2.png");
			hc = new HuffmanCode(ht);
			
			// with Huffman: code, compress, decode and save as .png
			test2PNGEncoded = hc.encryptImage(new ImageReader("test2.png"));
			hc.compress(1);
			BufferedImage test2PNGHF = hc.decryptImage(test2PNGEncoded, 8, 8);
			IO.saveImage("test2_HF_1", test2PNGHF);
			
			// with Huffman: use compressed tree from above and code and decode
			test2PNGEncoded = hc.encryptImage(new ImageReader("test2.png"));
			test2PNGHF = hc.decryptImage(test2PNGEncoded, 8, 8);
			IO.saveImage("test2_HF_2", test2PNGHF);
			// Frage 2 - ENDE
			
			// Frage 3 - START, Punkt 6. in PDF:
			ImageReader reader = new ImageReader("test.png");
			TrieCode tc = new TrieCode();
			// write the original image for comparison
			IO.writeImageBinaryString("orginal_test_F3.bin", reader);
			// build the Trie for encoding
			tc.buildTrie(reader);
			// write an uncompressed, encoded version of the image
			IO.writeBinaryString("uncompressed_encoded_test_F3.bin", tc.encryptImage(reader));
			tc.compress(3);
			// write an encoded,  3-tiered compression of teh image
			IO.writeBinaryString("three_compressed_encoded_test_F3.bin", tc.encryptImage(reader));
			tc.compress(6);
			// write an encoded,  6-tiered compression of teh image
			IO.writeBinaryString("six_compressed_encoded_test_F3.bin", tc.encryptImage(reader));
			// Frage 3 - ENDE
	
			// Frage 4 - START
			reader = new ImageReader("test.png");
			ImageReader reader2 = new ImageReader("test2.png");
			// create Trie encoding tree from test.png
			tc = new TrieCode();
			tc.buildTrie(reader);
			// encrypt and decrypt test2.png with the Trie encoding tree from test.png
			IO.saveImage("test_encoded_test2_F4", tc.decryptImage(tc.encryptImage(reader2), reader2.getHeight(), reader2.getWidth()));
			// Frage 4 - ENDE

			//STOOPID PHUN - START
			tc = new TrieCode();
			tc.buildTrie(reader2);
			// encrypt and decrypt test2.png with the Trie encoding tree from test.png
			IO.saveImage("STOOPID_PHUN", tc.decryptImage(tc.encryptImage(reader), reader.getHeight(), reader.getWidth()));
			// STOOPID PHUN - ENDE
			
			
			
			/*
			ht = HuffmanTree.getHuffmanTree("test.png");
			hc = new HuffmanCode(ht);
			String testPNGEncoded = hc.encryptImage(new ImageReader("test.png"));
			BufferedImage testPNGHF = hc.decryptImage(testPNGEncoded, 800, 600);
			IO.saveImage("test_HF", testPNGHF);
			*/
			
			
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
	}

}
